package com.qrxl.util;

import com.qrxl.common.exception.CustomException;
import com.qrxl.common.JGConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;


/**
 * AES加密工具类
 *
 * @author xieyuanqing
 * @date 2016/6/22
 */
@Slf4j
public class AesUtils {

    /**
     * LOG日志对象
     */
    private static final Log LOG = LogFactory.getLog(AesUtils.class);

    /**
     * 加密算法
     */
    private static final String CRYPTOGRAPHIC_ALGORITHM = "AES";

    /**
     * 加解密过程参数（‘密钥算法’、‘填充模式’、‘连接方式’，数据库中的加解密过程参数和此处一致）
     */
    private static final String AES_ECB_PKCS5 = "AES/ECB/PKCS5Padding";

    /**
     * 加密字符编码
     */
    private static final String CRYPTOGRAPHIC_CHARSETN = "gbk";

    /**
     * 随机数算法
     */
    private static final String SECURERANDOM_ALGORITHM = "SHA1PRNG";

    /**
     * 加解密key
     */
    private static byte[] aesDBKey;

    private AesUtils() {
    }

    static {
        try {
            //数据库专用密钥，其他不要使用
            String aesKey = JGConstant.AES_KEY;
            //SecureRandom secureRandom = SecureRandom.getInstance(SECURERANDOM_ALGORITHM);
            //secureRandom.setSeed(aesKey.getBytes("utf-8"));

            //KeyGenerator kgen = KeyGenerator.getInstance("AES");
            //kgen.init(128);

            //SecretKey secretKey = kgen.generateKey();
            //secretKey.getEncoded();
            aesDBKey = aesKey.getBytes("gbk");
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 加密器本地线程变量
     */
    private static ThreadLocal<Cipher> ecipherThreadLocal = new ThreadLocal<Cipher>() {
        @Override
        public Cipher initialValue() {
            Cipher ecipher = null;
            try {
                if (aesDBKey == null) {
                    throw new Exception("key is null,please have a check");
                }
                SecretKeySpec sks = new SecretKeySpec(aesDBKey, CRYPTOGRAPHIC_ALGORITHM);
                // 创建加密密码器
                ecipher = Cipher.getInstance(AES_ECB_PKCS5);
                ecipher.init(Cipher.ENCRYPT_MODE, sks);
            } catch (NoSuchAlgorithmException e) {
                LOG.error(e.getMessage(), e);
            } catch (NoSuchPaddingException e) {
                LOG.error(e.getMessage(), e);
            } catch (InvalidKeyException e) {
                LOG.error(e.getMessage(), e);
            } catch (UnsupportedEncodingException e) {
                LOG.error(e.getMessage(), e);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
            return ecipher;
        }
    };

    /**
     * 解密器本地线程变量
     */
    private static ThreadLocal<Cipher> dcipherThreadLocal = new ThreadLocal<Cipher>() {
        @Override
        public Cipher initialValue() {
            Cipher dcipher = null;
            try {
                if (aesDBKey == null) {
                    throw new Exception("key is null,please have a check");
                }

                SecretKeySpec sks = new SecretKeySpec(aesDBKey, CRYPTOGRAPHIC_ALGORITHM);
                // 创建解密密码器
                dcipher = Cipher.getInstance(AES_ECB_PKCS5);
                dcipher.init(Cipher.DECRYPT_MODE, sks);
            } catch (NoSuchAlgorithmException e) {
                LOG.error(e.getMessage(), e);
            } catch (NoSuchPaddingException e) {
                LOG.error(e.getMessage(), e);
            } catch (InvalidKeyException e) {
                LOG.error(e.getMessage(), e);
            } catch (UnsupportedEncodingException e) {
                LOG.error(e.getMessage(), e);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
            return dcipher;
        }
    };


    /**
     * 加密方法
     *
     * @param content 加密内容
     * @param key     密钥key
     * @return String
     */
    public static String encryptByKey(String content, String key) throws CustomException {
        if (StringUtils.isBlank(content)) {
            return null;
        }
        //去除前后空格
        content = content.trim();
        try {
            SecureRandom secureRandom = SecureRandom.getInstance(SECURERANDOM_ALGORITHM);
            secureRandom.setSeed(key.getBytes("utf-8"));

            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, secureRandom);

            SecretKey secretKey = kgen.generateKey();
            byte[] keyb = secretKey.getEncoded();
            SecretKeySpec sks = new SecretKeySpec(keyb, CRYPTOGRAPHIC_ALGORITHM);
            // 创建加密密码器
            Cipher ecipher = Cipher.getInstance(CRYPTOGRAPHIC_ALGORITHM);
            ecipher.init(Cipher.ENCRYPT_MODE, sks);
            byte[] byteContent = content.getBytes(CRYPTOGRAPHIC_CHARSETN);
            byte[] result = null;
            result = ecipher.doFinal(byteContent);
            // 16进制的转换
            return HexStringUtils.parseByte2HexStr(result);
        } catch (Exception e) {
            log.error("", e);
            throw new CustomException("加密失败,请联系管理员。");
        }
    }

    /**
     * 解密方法
     *
     * @param content 解密内容
     * @param key     密钥key
     * @return String
     */
    public static String decryptByKey(String content, String key) throws CustomException {
        if (StringUtils.isBlank(content)) {
            return null;
        }
        try {
            SecureRandom secureRandom = SecureRandom.getInstance(SECURERANDOM_ALGORITHM);
            secureRandom.setSeed(key.getBytes("utf-8"));

            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, secureRandom);

            SecretKey secretKey = kgen.generateKey();
            byte[] keyb = secretKey.getEncoded();
            SecretKeySpec sks = new SecretKeySpec(keyb, CRYPTOGRAPHIC_ALGORITHM);
            // 创建加密密码器
            Cipher dcipher = Cipher.getInstance(CRYPTOGRAPHIC_ALGORITHM);
            dcipher.init(Cipher.DECRYPT_MODE, sks);
            byte[] contentTemp = HexStringUtils.parseHexStr2Byte(content);
            byte[] result = null;
            result = dcipher.doFinal(contentTemp);
            return new String(result, CRYPTOGRAPHIC_CHARSETN);
        } catch (Exception e) {
            log.error("", e);
            throw new CustomException("解密失败,请联系管理员。");
        }
    }

    private static Cipher creatCipher() throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5);
        return cipher;
    }

    public static void main(String[] args) throws Exception {
        //数据库AES加密方法测试
        //明文小于16 byte 密码为32byte，大于16 byte，16*n长的原文会得到32*n长度的密文(原文等于16的倍数，N=N+1)
        //当n=1 明<16*n,密文=32*n; 当N>=1,当明文<16*N,密文=32*N; 当明文=16*N,密文=32*(N+1)
        /*String content = "12345678901234567";
        //String content = "this is my first Testthis is my first Testthis is my first Test";
        System.out.println("length " + content.length() + " byte.length" + content.getBytes("UTF-8").length
                + " content = " + content);
        //System.out.println("length " + HexStringUtils.stringToHex(content).length() +  "contentToHex = " + HexStringUtils.stringToHex(content));
        String encryptContent = encryptForDB(content);
        System.out.println("length " + encryptContent.length() + " encryptContent = " + encryptContent);
        System.out.println("decryptForDB = " + decryptForDB(encryptContent));
        System.out.println();*/

        //其他AES加密方法测试
        /*String aesKey = "6c616e73656e74313233343536373839";//lansent123456789
        String content2 = "其他AES加密方法测试：this is my first Test,我就是我，不一样的烟火!@#$%^&*()_+|";
        System.out.println("content2 = " + content2);
        String encryptContent2 = encryptByKey(content2, aesKey);
        System.out.println("encryptByKey = " + encryptContent2);
        System.out.println("decryptByKey = " + decryptByKey(encryptContent2, aesKey));*/


    }
}
